﻿/*
 * Navigation service
 *
 *
 * Issue:
 *      1. Bad SessionChanged Event fire when supporting multiple sessions actived
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Snowdreamist.Windows.Navigation.Primitives;

namespace Snowdreamist.Windows.Navigation
{
    public class NavigationService
    {
        public NavigationService(INavigationPresenter presenter)
        {
            if (presenter == null)
                throw new ArgumentNullException("presenter");
            this.Presenter = presenter;
            this.SessionManager = new SessionManager();
            this.HandlerRepository = new NavigationHandlerRepository();
            this.HandlerSelector = new NavigationHandlerSelector(this.HandlerRepository);
            this.HandlerInvoker = new NavigationHandlerInvoker();
            this.Switcher = new NavigationSwitcher();
            this.ProcessorFactory = new NavigateProcessorFactory(this.HandlerRepository, this.HandlerSelector, this.HandlerInvoker, this.Switcher);
        }

        public virtual Session NewSession(Uri uri, bool isActive = true)
        {
            // build session
            var session = this.BuildSession();
            // add session
            this.SessionManager.AddSession(session);
            // navigate to
            session.NavigateTo(uri);
            // active or not
            this.Presenter.AddSession(session);
            if (isActive)
                this.Presenter.GetSessionPresenter(session).Active();

            return session;
        }

        public virtual void CloseSession(Session session)
        {
            if (session == null)
                throw new ArgumentNullException("session");
            // Check service
            if (!object.ReferenceEquals(session.Service, this))
                throw new InvalidOperationException("Navigation service mismatch");
            // Remove
            this.SessionManager.RemoveSession(session);
            this.Presenter.RemoveSession(session);
        }

        /// <summary>
        /// Navigation error handler
        /// </summary>
        public INavigationHandler ErrorHandler { get; set; }

        /// <summary>
        /// Session manager
        /// </summary>
        public SessionManager SessionManager { get; private set; }

        public event EventHandler<SessionChangedEventArgs> SessionChanged;

        public event EventHandler<NavigationErrorEventArgs> NavigationError;

        public event EventHandler<NavigatingEventArgs> Navigating;

        public event EventHandler<NavigatedEventArgs> Navigated;

        #region internal

        /// <summary>
        /// Target NavigationFrame
        /// </summary>
        internal INavigationPresenter Presenter { get; private set; }

        internal NavigateProcessor CreateNavigateProcessor(Session session)
        {
            if (session == null)
                throw new ArgumentNullException("session");

            var processor = this.ProcessorFactory.CreateProcessor(session);

            return processor;
        }

        #endregion internal

        #region protected

        protected NavigationHandlerRepository HandlerRepository { get; private set; }

        protected NavigationHandlerSelector HandlerSelector { get; private set; }

        protected NavigationHandlerInvoker HandlerInvoker { get; private set; }

        protected NavigationSwitcher Switcher { get; private set; }

        protected NavigateProcessorFactory ProcessorFactory { get; private set; }

        protected virtual Session BuildSession()
        {
            return new Session(this);
        }

        /// <summary>
        /// Handle bad uri
        /// For http:// https:// ftp:// etc, these uris can handled by system internet explorer, just send navigation request is OK
        /// </summary>
        /// <param name="context"></param>
        /// <returns>True for handled</returns>
        protected virtual bool HandleBadUri(NavigationContext context)
        {
            // TODO
            return false;
        }

        protected virtual void OnNavigationError(Uri uri, Exception error)
        {
            if (this.NavigationError != null)
                this.NavigationError(this, new NavigationErrorEventArgs(uri, error));
        }

        protected virtual void OnNavigating(Uri uri)
        {
            if (this.Navigating != null)
                this.Navigating(this, new NavigatingEventArgs(uri));
        }

        protected virtual void OnNavigated(Uri uri, Exception error)
        {
            if (this.Navigated != null)
                this.Navigated(this, new NavigatedEventArgs(uri, error));
        }

        #endregion protected
    }
}